/*
 * Copyright (c) 2010-2013 Ruben Chakhmakhchyan. All rights reserved.
 * 
 * This file contains Original Code and/or Modifications of Original Code
 * as defined in and that are subject to the Eclipse Public License
 * Version 1.0 (the 'License'). You may not use this file except in
 * compliance with the License. Please obtain a copy of the License at
 * http://opensource.org/licenses/EPL-1.0 and read it before using this file.
 */

#include "pdf_document.h"
#include "pdf_writer.h"
#include "pdf_stream.h"
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include <Windows.h>


PdfDocument::PdfDocument()
    : m_autoSave( false )
{
    m_images.reserve( 128 );
}

PdfDocument::PdfDocument( wchar_t* filename )
    : m_filename( filename ), m_autoSave( true )
{
}

void PdfDocument::addImage( PdfImage* image )
{
    uint_t index = m_images.count() + 1;

    char name[ 16 ] = { 0 };

    ::sprintf( name, "Img%i", index );

    image->setName( name );

    m_images.push_back( image );
}

PdfObject* PdfDocument::getStockObject( PdfObject::Type type )
{
    switch( type )
    {
    case PdfObject::Type::Catalog:
        return &m_objCatalog;

    case PdfObject::Type::Pages:
        return &m_objPages;
    }

    return 0;
}

uint_t PdfDocument::getTotalCount()
{
    uint_t pages = m_objPages.getCount();
    uint_t images = m_images.size();

    return 2 /*catalog and pages dict*/ + ( pages << 1 ) /*pages and contents*/ + images /*images*/ + 1 /*info*/ + 1 /*xref*/;
}

void PdfDocument::generateIds()
{
    uint_t pages = m_objPages.getCount();

    uint_t baseIndex = 2 + ( pages << 1 ) + 1;

    // images
    for( auto i : m_images )
    {
        PdfImage& image = *i;
        image.setId( baseIndex++ );
    }

    // pages
    baseIndex = 3;

    const List<PdfPage*>& pagesArray = m_objPages.getPages();

    for( auto i : pagesArray )
    {
        PdfPage& page = *i;
        page.setId( baseIndex );
        baseIndex += 2;
    }
}

void PdfDocument::save( PdfStream& stream )
{
    generateIds();

    PdfWriter writer( stream );

    uint_t pages = m_objPages.getCount();
    uint_t images = m_images.size();

    uint_t objects = getTotalCount();
    List<uint_t> xrefs;
    xrefs.reserve( ( objects + 1 ) * sizeof( int ) );

    for ( uint_t i = 0; i < objects + 1; i++ )
    {
        xrefs.push_back( 0 );
    }

    // header
    writer.write( "%PDF-1.4\n%" ).write( 0xE2 ).write( 0xE3 ).write( 0xCF ).write( 0xD3 ).writeLF();

    // images
    uint_t baseIndex = 2 + ( pages << 1 );

    for( auto i : m_images )
    {
        xrefs[ baseIndex++ ] = writer.getPosition();

        PdfImage& image = *i;
        writer.writeObject( image );

        // memory cleanup
        image.clear();
    }

    // catalog
    xrefs[ 0 ] = writer.getPosition();

    writer.writeObject( *getStockObject( PdfObject::Type::Catalog ) );

    // pages index
    xrefs[ 1 ] = writer.getPosition();

    writer.writeObject( *getStockObject( PdfObject::Type::Pages ) );

    // pages
    baseIndex = 2;

    const List<PdfPage*>& pagesArray = m_objPages.getPages();

    for( auto i : pagesArray )
    {
        xrefs[ baseIndex ] = writer.getPosition();

        PdfPage& page = *i;
        writer.writeObject( page );

        xrefs[ baseIndex + 1 ] = writer.getPosition();

        PdfPageContent pageContent( &page );
        writer.writeObject( pageContent );

        baseIndex += 2;
    }

    /*time_t time = { 0 };
    ::time( &time );
    struct tm *ti;
    ti = localtime( &time );*/ // wrong implementation

    SYSTEMTIME st = { 0 };
    ::GetLocalTime( &st );


    // info
    xrefs[ objects - 2 ] = writer.getPosition();
    writer.writeInt( objects - 1 ).write( " 0 obj\n<< \n /Creator (Web Browser)\n /Producer (Skan Plugin)\n /CreationDate (D:" ).writeInt( st.wYear ).writeInt( st.wMonth, 2 ).writeInt( st.wDay, 2 ).writeInt( st.wHour, 2 ).writeInt( st.wMinute, 2 ).writeInt( st.wSecond, 2 ).write( "+00'00')\n>>\nendobj" ).writeLF(); //20120114140135 //TODO BUG


    // xref, trailer
    xrefs[ objects - 1 ] = writer.getPosition();

    writer.write( "xref\n0 " ).writeInt( objects ).write( " \n0000000000 65535 f \n" );
    for( uint_t i = 0; i < objects - 1; i++ )
    {
        writer.writeInt( xrefs[ i ], 10 ).write( " 00000 n \n" );
    }

    writer.write( "trailer\n<< \n /Root 1 0 R /Info ").writeInt( objects - 1 ).write(" 0 R /Size " ).writeInt( objects ).write( "\n>>\nstartxref\n" ).writeInt( xrefs[ objects - 1 ] ).write( "\n%%EOF\n" );
}
